<template>
  <div class="super-table-page" ref="superTable">
    <div class="m-header-wrapper">
      <div
        class="m-header-view"
        :style="{ width: tbodyWidth ? `${tbodyWidth}px` : '100%' }"
      >
        <div :class="['m-header-left',scrollLeft === 0 ? '':'has-left-shadow']">
          <!-- 选择框   -->
          <div class="row-selection m-header-cell" v-if="hasRowSelection">
            <input
              id="superTableCheckAll"
              type="checkbox"
              v-model="checkAll"
              @change="checkAllHandler"
            />
          </div>
          <m-header-cell
            v-for="(header, index) in columnLeft"
            :key="index"
            :header="header"
            :sortables="sortables"
            @updateLayout="updateLayout"
          ></m-header-cell>
        </div>
        <div class="m-header-center">
          <m-header-cell
            v-for="(header, index) in columnCenter"
            :key="index"
            :header="header"
            :sortables="sortables"
            @updateLayout="updateLayout"
            :lastHeader="index === columnCenter.length-1"
          ></m-header-cell>
        </div>
        <div
          :class="[
            'm-header-right',
            scrollRight >= 5 ? 'has-right-shadow' : '',
          ]"
        >
          <m-header-cell
            v-for="(header, index) in columnRight"
            :key="index"
            :header="header"
            :sortables="sortables"
            @updateLayout="updateLayout"
            :lastHeader="index === columnRight.length-1"
          ></m-header-cell>
          <div class="scroll-placeholder"></div>
        </div>
      </div>
    </div>
    <div class="m-tbody-wrapper" :style="tableHeight">
      <div class="m-tbody">
        <div
          class="m-tbody-view"
          :style="{
            height: `${tbodyHeight}px`,
            width: tbodyWidth ? `${tbodyWidth}px` : '100%',
          }"
        >
          <div :class="['m-tbody-left',scrollLeft === 0 ? '':'has-left-shadow']">
            <m-tr
              v-for="(row, index) in showData"
              :key="index"
              :scrollYDistance="scrollYDistance"
            >
              <template #rowSelection>
                <div class="row-selection m-td" v-if="hasRowSelection">
                  <input
                    v-model="tableData[sIndex + index].isCheck"
                    @change="checkRowHandler(row,$event)"
                    type="checkbox"
                  />
                </div>
              </template>
              <template>
                <m-td
                  v-for="(td, Index) in columnLeft"
                  :key="`${index}-${Index}`"
                  :column="td"
                  :row="row"
                  :index="sIndex+index"
                >
                  <template #action="{scope}">
                    <slot
                        v-bind:scope="{ row:scope.row, column: scope.column, index:scope.index }"
                        name="action"
                      ></slot>
                  </template>
                </m-td>
              </template>
            </m-tr>
          </div>
          <div class="m-tbody-center">
            <m-tr
              v-for="(row, index) in showData"
              :key="index"
              :scrollYDistance="scrollYDistance"
            >
              <template>
                <m-td
                  v-for="(td, Index) in columnCenter"
                  :key="`${index}-${Index}`"
                  :column="td"
                  :row="row"
                  :index="sIndex+index"
                >
                  <template #action="{scope}">
                    <slot
                        v-bind:scope="{ row:scope.row, column: scope.column, index:scope.index }"
                        name="action"
                      ></slot>
                  </template>
                </m-td>
              </template>
            </m-tr>
          </div>
          <div
            :class="[
              'm-tbody-right',
              scrollRight >= 5 ? 'has-right-shadow' : '',
            ]"
          >
            <m-tr
              v-for="(row, index) in showData"
              :key="index"
              :scrollYDistance="scrollYDistance"
            >
              <template>
                <m-td
                  v-for="(td, Index) in columnRight"
                  :key="`${index}-${Index}`"
                  :column="td"
                  :row="row"
                  :index="sIndex+index"
                >
                  <template #action="{scope}">
                    <slot
                        v-bind:scope="{ row:scope.row, column: scope.column, index:scope.index }"
                        name="action"
                      ></slot>
                  </template>
                </m-td>
              </template>
            </m-tr>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import MTr from './m-tr.vue'
import MTd from './m-td.vue'
import MHeaderCell from './m-header-cell.vue'
export default {
  name: "SuperTable",
  components:{
    MTd,
    MTr,
    MHeaderCell,
  },
  provide:function(){
    return {
      setWidth: this.setWidth,
      clickHeaderCell: this.clickHeaderCell
    }
  },
  props: {
    rowKey: {
      type: String,
      default: () => 'id'
    },
    scroll: {
      type: [Number, String],
      default: () => 350,
    },
    height: {
      type: Number,
      default: () => 300,
    },
    column: {
      type: Array,
      required: true
    },
    selectionRowCache:{
      type: Map,
      default: () => new Map()
    },
    tableData:{
      type: Array,
      default: () => []
    },
  },
  data() {
    return {
      hasScrollX: false,
      scrollLeft: 0, // X轴滚动条左滚动距离
      scrollRight: 5,
      scrollYDistance: 0, // tbody Y平移的距离
      scrollXDistance: 0,
      tbodyHeight: 500, //  tbody的高度
      tbodyWidth: 0, // tobdy 宽度
      lastTime: "",
      showDataNum: 0, // 展示数据条数
      sIndex: 0,
      eIndex: 0,
      checkAll: false, // 全选
      selectionRow: [], // 勾选项
      showData: [],
      copyStutas: false, // 复制状态
      columnLeft: [],
      columnCenter: [],
      columnRight: [],
      sortables: {
        order: "", // desc ro asc
        props: "",
        count: 0,
      },
    };
  },
  created() {
    this.initConfig()
  },
  mounted() {
    // 计算高度
    this.tbodyHeight = this.tableData.length * 50;
    // 计算宽度
    this.isScrollX();
    this.initTable();
  },
  watch:{
    tableData(v){
      let checkCount = 0
      v.forEach(row=>{
        if(this.selectionRowCache.get(row[this.rowKey])){
          checkCount++
          row.isCheck = true
        }else{
          row.isCheck = false
        }
      })
      this.initCheckAll(checkCount)
      this.initData()
    }
  },
  computed: {
    // 是否有选择框
    hasRowSelection() {
      return (
        Object.prototype.toString.call(this.$listeners["selection-change"]) ===
        "[object Function]"
      );
    },
    // 展示区高度
    tableHeight() {
      return {
        height: `${this.height}px`,
      };
    },
  },
  methods: {
    updateLayout(){
      this.isScrollX()
    },
    initConfig(){
      this.showDataNum = Math.ceil(this.height / 50) + 4; //可视区域展示的数据条数
      this.showData = this.tableData.slice(0, this.showDataNum); // 初始化数据
      this.eIndex = this.showDataNum;
      this.lastTime = new Date().getTime(); // 获取一个时间搓
      this.columnCenter = this.column.filter((c) => !c.fixed);
      this.columnLeft = this.column.filter((c) => c.fixed === "left");
      this.columnRight = this.column.filter((c) => c.fixed === "right");
    },
    initTable(){
      let scrollWrapper = document.querySelector(".m-tbody-wrapper");
      let headerView = document.querySelector(".m-header-view");
      // 监听滚动
      scrollWrapper.addEventListener(
        "scroll",
        (e) => {
          // header 同步必须及时同步，不能防抖，否则会偶尔出现错位现象
          let scrollLeft = e.target.scrollLeft; // wrapper X轴滚动距离
          headerView.scrollLeft = scrollLeft; // header同步滚动
          this.scrollLeft = scrollLeft;
          this.scrollRight = this.tbodyWidth - (Math.ceil(scrollLeft) + e.target.offsetWidth - 17);
          if (new Date().getTime() > this.lastTime + 10) {
            let scrollTop = e.target.scrollTop; // wrapper容器滚动的高度
            this.scrollYDistance = scrollTop - (scrollTop % 50); // tbody需要同步平移wrapper容器滚动的距离
            this.scrollXDistance = scrollLeft
            this.sIndex = Math.floor(scrollTop / 50);
            this.eIndex = this.sIndex + this.showDataNum;
            this.showData = this.tableData.slice(this.sIndex, this.eIndex);
            this.lastTime = new Date().getTime();
          }
        },
        false
      );
    },
    initCheckAll(checkCount){
      const checkAll = document.getElementById("superTableCheckAll");
      this.checkAll = checkCount === this.tableData.length
      if(this.checkAll) checkAll.indeterminate = false
      else checkAll.indeterminate = (this.selectionRowCache.size > 0)
    },
    // 是否需要横向滚动条
    isScrollX() {
      const wrapperWidth = this.$refs.superTable.getBoundingClientRect().width;
      const dataWidth = this.getColumnTotalWidth(this.column) + 35
      if (wrapperWidth < dataWidth) {
        this.tbodyWidth = dataWidth;
        this.hasScrollX = true
      }else{
        this.tbodyWidth = 0;
        this.hasScrollX = false
      }
    },
    getColumnTotalWidth(array){
      let total = 0
      array.forEach(c=>{
        if(c.width) total+=c.width
        else{
          if(c.children){
            total += this.getColumnTotalWidth(c.children,total)
          }else{
            total += c.title.length * 12 + 14 + 24
          }
        }
      })
      return total
    },
    // 设置表头宽度
    setWidth(row) {
      if (row.width) {
        return {
          width: `${row.width}px`,
        };
      } else {
        if(row.fixed) {
          return {
            width: `${row.title.length * 12 + 14 + 24}px`,
          }
        }else{
          if(!this.hasScrollX) {
            return {
              flex: 1
            }
          }else{
            if(row.children) return {flex:1}
            return {
              width: `${row.title.length * 12 + 14 + 24}px`,
            }
          }
        }
       ;
      }
    },
    initData() {
      let scrollWrapper = document.querySelector(".m-tbody-wrapper");
      scrollWrapper.scrollTop = 0
      this.sIndex = 0
      this.eIndex = this.sIndex + this.showDataNum;
      this.showData = this.tableData.slice(this.sIndex, this.eIndex);
    },
    // 排序
    sortData() {
      let { order, props } = this.sortables;
      if (this.$listeners["sort-change"]) {
        this.$emit("sort-change", { order, props });
      } else {
        this.tableData = this.tableData.sort((a, b) => {
          if (order === "asc")
            return (
              String(a[props]).charCodeAt(0) - String(b[props]).charCodeAt(0)
            );
          else if (order === "desc")
            return (
              String(b[props]).charCodeAt(0) - String(a[props]).charCodeAt(0)
            );
        });
        this.showData = this.tableData.slice(this.sIndex, this.eIndex);
      }
    },
    // 点击表头事件
    clickHeaderCell(row) {
      if (!row.sortable && !row.dataIndex) return;
      let { props, count } = this.sortables;
      if (props) {
        if (props === row.sortable || props === row.dataIndex) {
          if (count === 0) {
            this.sortables.order = "asc";
            this.sortables.count++;
          } else if (count === 1) {
            this.sortables.order = "desc";
            this.sortables.count++;
          } else if (count === 2) {
            this.sortables.order = "";
            this.sortables.count = 0;
            this.sortables.props = "";
          }
        } else {
          // 重置
          this.sortables.order = "asc";
          this.sortables.count = 1;
        }
      } else {
        if (count === 0) {
          this.sortables.order = "asc";
          this.sortables.count++;
        } else if (count === 1) {
          this.sortables.order = "desc";
          this.sortables.count++;
        } else if (count === 2) {
          this.sortables.order = "";
          this.sortables.count = 0;
          this.sortables.props = "";
        }
      }
      this.sortables.props = row.sortable || row.dataIndex;
      this.sortData();
    },
    // 点击选择某项
    checkRowHandler(e) {
      const rowKey = this.rowKey
      const checkAll = document.getElementById("superTableCheckAll");
      const checkCount = this.tableData.filter((t) => t.isCheck);
      if(e.isCheck) this.selectionRowCache.set(e[rowKey],e[rowKey])
      else this.selectionRowCache.delete(e[rowKey])
      this.checkAll = checkCount.length === this.tableData.length;
      checkAll.indeterminate =
        checkCount.length > 0 && checkCount.length < this.tableData.length;
      this.showData = this.tableData.slice(this.sIndex, this.eIndex);
      this.$emit("selection-change", checkCount);
    },
    // 点击全选
    checkAllHandler(v) {
      const rowKey = this.rowKey
      const checked = v.target.checked;
      const checkAll = document.getElementById("superTableCheckAll");
      this.selectionRow = v ? this.tableData : [];
      this.tableData.forEach((r) => {
        r.isCheck = checked
        if(checked) this.selectionRowCache.set(r[rowKey],r[rowKey])
        else this.selectionRowCache.delete(r[rowKey])
      });
      checkAll.indeterminate = false;
      this.showData = this.tableData.slice(this.sIndex, this.eIndex);
      this.$emit("selection-change", checked ? this.tableData : []);
    },
  },
};
</script>

<style lang="scss" scoped>
.super-table-page {
  margin-bottom: 50px;
  border-right: 1px solid #ccc;
  border-top: 1px solid #ccc;
  border-left: 1px solid #ccc;
  background-color: #fafafa;
  .has-right-shadow {
    box-shadow: -2px 0px 5px #e1e1e1;
  }
  .has-left-shadow {
    box-shadow: 2px 0px 5px #e1e1e1;
  }
  .m-header-wrapper {
    position: relative;
    border-bottom: 1px solid #ccc;
    //height: 50px;
    background-color: #fafafa;
    display: flex;
    overflow-y: hidden;
    .m-header-view::-webkit-scrollbar {
      display: none;
    }
    .m-header-view {
      display: flex;
      //height: 50px;
      overflow-x: auto;
      .scroll-placeholder:after {
        content: "";
        padding-right: 17px;
      }
      .m-header-left,
      .m-header-center,
      .m-header-right {
        display: flex;
        .m-header-cell {
          height: 100%;
          padding: 0 12px;
          display: flex;
          align-items: center;
          font-weight: 600;
          border-right: 1px solid #ccc;
          // overflow: hidden;
          // text-overflow: ellipsis;
          // white-space: nowrap;
          cursor: pointer;
          > span {
            display: flex;
            flex: 1;
            justify-content: space-between;
            .text-content {
              display: flex;
              align-items: center;
            }
            .operation-icon {
              .sort-icon {
                color: #ccc;
                display: block;
                line-height: 8px;
              }
              .sort-icon:hover {
                color: #aaa;
              }
              .active-sort-icon {
                color: #1890ff !important;
              }
            }
          }
        }
      }
      .m-header-left,
      .m-header-right {
        position: sticky;
        z-index: 4;
        background: #fafafa;
      }
      .m-header-left {
        left: 0;
      }
      .m-header-right {
        right: 0;
        border-left: 1px solid #ccc;
      }
      .m-header-center {
        flex: 1;
      }
    }
  }
  .m-tbody-wrapper {
    min-height: 80px;
    // height: 300px;
    // border-left: 1px solid #ccc;
    border-bottom: 1px solid #ccc;
    //border-right: 1px solid #ccc;
    overflow: auto;
    .m-tbody {
      .m-tbody-view {
        display: flex;
        .m-tbody-left,
        .m-tbody-center,
        .m-tbody-right {
          background-color: #fff;
          .m-tr {
            display: flex;
            border-bottom: 1px solid #ccc;
            align-items: center;
            .m-td {
              padding: 0 12px;
              line-height: 50px;
              border-right: 1px solid #ccc;
              height: 50px;
              > div {
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
              }
            }
          }
          .m-tr:last-child {
            border-bottom: none;
          }
        }
        .m-tbody-left,
        .m-tbody-right {
          position: sticky;
          z-index: 4;
        }
        .m-tbody-left {
          left: 0;
        }
        .m-tbody-right {
          right: 0;
          border-left: 1px solid #ccc;
        }
        .m-tbody-center {
          flex: 1;
        }
      }
    }
  }
  .row-selection {
    width: 35px;
  }
}
</style>
